﻿using AppGauge.Data.SqlServer;
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;

namespace AppGauge.Data.SqlServer.Fluent
{

    #region [ Mapper Packet Definition ]
    /// <summary>
    /// Table Attribute
    /// </summary>
    [System.AttributeUsage(System.AttributeTargets.Class)]
    public class Table : System.Attribute
    {
        /// <summary>
        /// The table name
        /// </summary>
        string tableName;

        /// <summary>
        /// Initializes a new instance of the <see cref="Table"/> class.
        /// </summary>
        /// <param name="TableName">Name of the table.</param>
        public Table(string TableName)
        {
            if (TableName != null && TableName.Length > 2 && TableName.Length < 100)
                this.tableName = TableName;

        }

        /// <summary>
        /// Gets the name of the table.
        /// </summary>
        /// <returns></returns>
        public string GetTableName()
        {
            return tableName;
        }

    }

    /// <summary>
    /// Column Attribute
    /// </summary>
    [System.AttributeUsage(System.AttributeTargets.Property)]
    public class Column : System.Attribute
    {
        /* 
         // For Future Use
         int columnSize;
         int identity;//Auto Increment;
         float minSize, maxSize;
         bool allowNull;
         string defaultValue;
         string tableName;
         */
        /// <summary>
        /// The column name
        /// </summary>
        string columnName;
        /// <summary>
        /// The primary key
        /// </summary>
        bool primaryKey;

        /// <summary>
        /// Initializes a new instance of the <see cref="Column"/> class.
        /// </summary>
        /// <param name="Name">The name.</param>
        /// <param name="Key">if set to <c>true</c> [key].</param>
        public Column(string Name, bool Key = false)
        {
            columnName = Name;
            this.primaryKey = Key;
        }
        /// <summary>
        /// Gets the columns.
        /// </summary>
        /// <returns></returns>
        public string[] GetColumns()
        {
            string[] n = { columnName, primaryKey == true ? "Key" : null };
            return n;
        }
    }

    #endregion

    #region [ Mapper Service Provider ]

    /// <summary>
    /// AppGauge Fluent Service Provider Class. Includes Responder, Fluent Methods and Containment Blocks.
    /// </summary>
    public class AppData
    {
        DataContainer dc;
        DataTable dt;
        Dictionary<string, string> dataMap;
        string connectionString;

        /// <summary>
        /// Gets or sets the connection string.
        /// </summary>
        /// <value>
        /// The connection string.
        /// </value>
        public string ConnectionString
        {
            get { return connectionString; }
            set
            {
                if (value != null && value.Length > 10 && value.Length < 1000)
                    connectionString = value;
                else
                {
                    AppDebug.Log("Invalid Range. Must be within 10 to 1000 Characters.");
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AppData" /> class.
        /// </summary>
        /// <param name="ConStr">The con STR.</param>
        public AppData(string ConStr)
        {
            dc = new DataContainer();
            //dataMap is the local Data Mapper Strip. It Contains Property-Column Name pair.
            dataMap = new Dictionary<string, string>();
            FetchMap();

            ConnectionString = ConStr;
            dc.ConnectionString = this.ConnectionString;
        }

        #region [ AMS ( Automated Model Setup ) ]

        /// <summary>
        /// Sets the props.
        /// </summary>
        /// <param name="PropertyName">Name of the property.</param>
        /// <param name="Value">The value.</param>
        /// <returns></returns>
        private bool SetProps(string PropertyName, string Value)
        {
            try
            {
                PropertyInfo pr = this.GetType().GetProperty(PropertyName);
                pr.SetValue(this, Convert.ChangeType(Value, pr.PropertyType), null);
                return true;
            }
            catch (Exception e)
            {
                AppDebug.Log(e.ToString(), "Property Set Error");
                return false;
            }
        }

        /// <summary>
        /// Gets the props.
        /// </summary>
        /// <param name="PropertyName">Name of the property.</param>
        /// <returns></returns>
        private object GetProps(string PropertyName)
        {
            try
            {
                PropertyInfo pr = this.GetType().GetProperty(PropertyName);
                return pr.GetValue(this, null);
            }
            catch (Exception e)
            {
                AppDebug.Log(e.ToString(), "Property Set Error");
                return null;
            }
        }

        /// <summary>
        /// Gets the current key int value.
        /// </summary>
        /// <returns></returns>
        private int GetCurrentKeyIntValue()
        {
            try
            {
                return Convert.ToInt32(GetProps(dataMap[dc.TKey]));
            }
            catch (Exception e)
            {
                AppDebug.Log(e.ToString(), "Property Get Error");
                return -1;
            }
        }

        /// <summary>
        /// Sets the current key int value.
        /// </summary>
        /// <param name="Value">The value.</param>
        /// <returns></returns>
        private bool SetCurrentKeyIntValue(int Value)
        {
            try
            {
                SetProps(dataMap[dc.TKey], Value.ToString());
                return true;
            }
            catch (Exception e)
            {
                AppDebug.Log(e.ToString(), "Property Get Error");
                return false;
            }
        }

        /// <summary>
        /// Fetches the map.
        /// </summary>
        /// <returns></returns>
        private int FetchMap()
        {
            int i = 0;
            System.Attribute[] t = System.Attribute.GetCustomAttributes(this.GetType());
            Table m = (Table)t[0];
            List<string> lst = new List<string>();

            //Setting Table Name & Connection String
            dc.Table = m.GetTableName().ToString();

            //Fetching Columns
            foreach (PropertyInfo pr in this.GetType().GetProperties())
            {
                i++;
                Object[] attr = pr.GetCustomAttributes(typeof(Column), true);
                if (attr.Length > 0)
                {
                    Column col = (Column)attr[0];
                    string[] c = col.GetColumns();
                    lst.Add(c[0].ToString());
                    dataMap.Add(c[0], pr.Name);

                    //Setting Table Key
                    if (c[1] != string.Empty) dc.TKey = c[0].ToString();
                    // Console.WriteLine(pr.Name + " = " + pr.GetValue(this, null) + "\n");

                }
            }

            //Setting Colulmns
            dc.Columns = lst.ToArray();
            return i;
        }

        #endregion

        #region [ Fluent Methods ]
        /// <summary>
        /// Views the specified id.
        /// </summary>
        /// <param name="Id">The id.</param>
        /// <returns>True if OK.</returns>
        /// <example>
        /// <code>
        ///     View(1); // Loads all properties with Id 1
        /// </code>
        /// </example>
        public bool View(int Id)
        {
            if (dc.Exists(Id.ToString(), "bookId"))
            {
                dt = new DataTable();
                dt = dc.View(Id.ToString());
                string[,] fld = new string[1, 2];

                foreach (DataColumn dcol in dt.Columns)
                {
                    SetProps(dataMap[dcol.ToString()], dt.Rows[0][dcol].ToString());
                }
                return true;
            }
            else
            {
                AppDebug.Log("ID not found!");
                return false;
            }
        }

        /// <summary>
        /// Gets all.
        /// </summary>
        /// <returns></returns>
        public DataTable GetAll()
        {
            return dc.View();
        }

        /// <summary>
        /// Saves this instance.
        /// </summary>
        /// <returns></returns>
        public bool Save()
        {

            List<string> valAr = new List<string>();
            foreach (KeyValuePair<string, string> p in dataMap)
                valAr.Add(GetProps(p.Value).ToString());

            valAr.RemoveAt(0);

            if (GetCurrentKeyIntValue() == 0)
            {
                //Insert
                try
                {
                    int id = dc.Insert(valAr.ToArray());
                    SetCurrentKeyIntValue(id);
                    return true;
                }
                catch (Exception e)
                {
                    AppDebug.Log(e.ToString(), "Insert Error");
                    return false;
                }
            }
            else
            {
                //Update
                try
                {
                    return dc.Update(valAr.ToArray(), GetCurrentKeyIntValue().ToString());
                }
                catch (Exception e)
                {
                    AppDebug.Log(e.ToString(), "Insert Error");
                    return false;
                }
            }
        }

        /// <summary>
        /// Deletes this instance.
        /// </summary>
        /// <returns></returns>
        public bool Delete()
        {
            if (dc.Exists(GetCurrentKeyIntValue().ToString(), dc.TKey))
            {
                return dc.Delete(GetCurrentKeyIntValue().ToString());
            }
            else
            {
                AppDebug.Log("Id not found!");
                return false;
            }
        }

        #endregion

    }

    #endregion

}
